﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DARE;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Text.RegularExpressions;

namespace DARE
{
    public class CDareDataStructuresMgr
    {
        #region fields

        [ContentSerializer]
        private Dictionary<string, CCameraData> m_cameras;
        [ContentSerializer]
        private CAudioMgrData m_audioMgr;
        [ContentSerializer]
        private CCursorData m_cursor;
        [ContentSerializer]
        private Dictionary<string, CNodeData> m_nodes;
        [ContentSerializer]
        private Dictionary<string, CTerrainData> m_terrains;
        [ContentSerializer]
        private Dictionary<string, CWidgetData> Widgets;
        [ContentSerializer]
        private Dictionary<string, CGameObjectData> m_gameObjects;
        [ContentSerializer]
        private Dictionary<uint, CGameObjectData> m_instances;
        [ContentSerializer]
        private CEffectMgr m_effectMgr;
        [ContentSerializer]
        private string HUDName;
        [ContentSerializer]
        private string m_renderName;

        #endregion

        #region properties
        #endregion

        #region ctor
        
        public CDareDataStructuresMgr()
        {
            m_cameras = new Dictionary<string, CCameraData>();
            m_nodes = new Dictionary<string, CNodeData>();
            Widgets = new Dictionary<string, CWidgetData>();
            m_gameObjects = new Dictionary<string, CGameObjectData>();
            m_instances = new Dictionary<uint, CGameObjectData>();
            m_audioMgr = new CAudioMgrData();
            m_terrains = new Dictionary<string, CTerrainData>();
        }
        
        #endregion

        #region methods

        #region construction

        public CDareDataStructuresMgr GenerateAll(CHUDSystem hud, CRender3D render, CScene scene)
        {
            if (render != null)
                GenerateRender3D(render);
            if (hud != null)
                GenerateHUD(ref hud);
            if (scene != null)
                GenerateGameObjects(scene, render, hud);
            return this;
        }

        public CDareDataStructuresMgr GenerateGameObjects(CScene scene, CRender3D render, CHUDSystem hud)
        {
            foreach (CGameObjectData data in m_gameObjects.Values)
            {
                CGameObject obj = data.gameObject;
                data.FillFromDataStructure(ref obj, render, hud);
                render.GameObjectMgr.RegisterGameObject(obj);
            }

            List<CGameObjectData> goData = new List<CGameObjectData>();
            foreach (CGameObjectData data in m_instances.Values)
            {
                uint id = data.gameObject.ID;
                render.GameObjectMgr.RegisterInstantiation(data.gameObject);
                data.gameObject.ID = id;
                goData.Add(data);
            }

            foreach (CGameObjectData data in goData)
            {
                CGameObject obj = render.GameObjectMgr.FindInstance(data.gameObject.ID);
                data.FillFromDataStructure(ref obj, render, hud);
            }
            return this;
        }

        public CDareDataStructuresMgr GenerateRender3D(CRender3D render)
        {
            if (m_renderName != null)
                render.Name = m_renderName;

            render.EffectMgr = m_effectMgr;

            //audio
            //nodes
            //render.HeightMapInfo = null;
            foreach (CNodeData nodeData in m_nodes.Values)
                render.CreateNode(nodeData.name, EType.STATIC, true);
            foreach (CNodeData nodeData in m_nodes.Values)
                FillNode(nodeData, render);
            foreach (CTerrainData terrain in m_terrains.Values)
                terrain.FillFromDataStructure(render);

            //camera
            FillCameras(ref render);
            return this;
        }

        /// <summary>
        /// This method fill the CHUDSystem given in parameters with the data contained.
        /// </summary>
        /// <param name="hud">the hud to be filled</param>
        /// <returns></returns>
        public CDareDataStructuresMgr GenerateHUD(ref CHUDSystem hud)
        {
            if (HUDName != null)
                hud.Name = HUDName;
            CCursor c = new CCursor(hud);
            m_cursor.FillFromDataStructure(c);
            Dictionary<CWidgetData, CWidget> wids = new Dictionary<CWidgetData, CWidget>();
            foreach (CWidgetData widData in Widgets.Values)
            {
                Type t = Type.GetType(widData.typeFullName);
                if (t == null)
                    t = typeof(CWidget);

                CWidget wid = hud.GetElement(widData.name);
                if (wid == null)
                {
                    System.Reflection.ConstructorInfo ci = t.GetConstructor(new Type[3] { typeof(DARE.CHUDSystem), typeof(CWidget), typeof(string) });
                    wid = (CWidget)ci.Invoke(new object[3] { hud, null, widData.name });
                }
                bool replace = false;
                foreach (CWidgetData wdata in wids.Keys)
                    if (wdata.name == wid.Name)
                    {
                        hud.Remove(wdata.name);
                        hud.Add(wid);
                        wid.HUD = hud;
                        wids[wdata] = wid;
                        replace = true;
                        break;
                    }
                if (!replace)
                    wids.Add(widData, wid);
            }

            foreach (KeyValuePair<CWidgetData, CWidget> wid in wids)
            {
                CWidget w = wid.Value;
                wid.Key.FillFromDataStructure(ref w, hud);
            }
            
            return this;
        }



        #region nodes

        private void FillNode(CNodeData nodeData, CRender3D render)
        {
            // node creation
            CNode node = render.GetNode(nodeData.name);
            if (node == null)
                node = render.OriginNode.CreateChild(nodeData.name, nodeData.Type);
            if (nodeData.parentName != null && nodeData.parentName != node.Parent.Name)
                node.SetParent(nodeData.parentName);
            node.Type = nodeData.Type;
            node.SetPosition(nodeData.position, CNode.ESpace.WORLD);
            node.SetOrientation(nodeData.orientation, CNode.ESpace.WORLD);
            node.SetScale(nodeData.scale);
            if (nodeData.modelData != null)
                FillModel(nodeData.modelData, node);
            if (nodeData.physicObjectData != null)
                FillPhysic(nodeData.physicObjectData, node);
            if (nodeData.basicModelData != null)
                FillBasicModel(nodeData.basicModelData, node);
            if (nodeData.audioEntityData != null)
                FillAudioSound(nodeData.audioEntityData, node);
            if (nodeData.emitterData != null)
                FillEmitters(nodeData.emitterData, node);
            if (nodeData.lineText3DData != null)
                FillSprite3D(nodeData.lineText3DData, node);
            node.IsActive = nodeData.isActive;
            node.AABBFromWorldBoundingBox = nodeData.aabb;
        }

        private void FillModel(CModelData modelData, CNode dareNode)
        {
            if ((modelData.name != null) && (modelData.path != null))
            {
                CModel model = new CModel(modelData.name, modelData.path);
                modelData.FillFromDataStructure(model);
                dareNode.Entity.AttachModel(model);
            }
        }

        private void FillPhysic(CPhysicObjectData physicObjectData, CNode dareNode)
        {
            APhysicObject physicObject = null;
            CModel model = null;

            switch (physicObjectData.physicObject.GetType().Name)
            {
                case "CPlaneData":
                    physicObject = dareNode.Entity.CreatePhysicPlane((physicObjectData.physicObject as CPlaneData).size).Physic;
                    break;

                case "CBoxData":
                    physicObject = dareNode.Entity.CreatePhysicBox((physicObjectData.physicObject as CBoxData).size).Physic;
                    break;

                case "CCylinderData":
                    physicObject = dareNode.Entity.CreatePhysicCylinder((physicObjectData.physicObject as CCylinderData).length,
                                                                        (physicObjectData.physicObject as CCylinderData).diameter).Physic;
                    break;

                case "CHeightMapData":
                    //dareNode.Render3D.HeightMapInfo = new CHeightMapInfo(CResourceMgr.Load<Texture2D>((physicObjectData.physicObject as CHeightMapData).textureName),
                    //                                        (physicObjectData.physicObject as CHeightMapData).terrainBump,
                    //                                        (physicObjectData.physicObject as CHeightMapData).terrainScale);
                    //physicObject = dareNode.Entity.CreatePhysicHeightmap(dareNode.Render3D.HeightMapInfo).Physic;
                    break;

                case "CSphereData":
                    physicObject = dareNode.Entity.CreatePhysicSphere((physicObjectData.physicObject as CSphereData).diameter).Physic;
                    break;

                case "CCapsuleData":
                    physicObject = dareNode.Entity.CreatePhysicCapsule((physicObjectData.physicObject as CCapsuleData).length,
                                                                        (physicObjectData.physicObject as CCapsuleData).diameter).Physic;
                    break;
                case "CTriangleData":
                    bool includeNonColl = false;
                    model = dareNode.Entity.Model as CModel;
                    if (model != null)
                    {
                        includeNonColl = (physicObjectData.physicObject as CTriangleData).includeNonColl;
                        physicObject = new CPhysicMesh(model, includeNonColl);
                        dareNode.Entity.AttachPhysic(physicObject);
                    }
                    else
                        Console.WriteLine("A triangle mesh collider cannot be created on node " + dareNode.Name
                            + " because it has no CModel available.");
                    break;
                case "CApproxData":
                    model = dareNode.Entity.Model as CModel;
                    if (model != null)
                    {
                        physicObject = new CPhysicMeshApprox(model);
                        dareNode.Entity.AttachPhysic(physicObject);
                    }
                    else
                        Console.WriteLine("An approximative bounding box cannot be created on node " + dareNode.Name
                            + " becaise is has no CModel available.");
                    break;
            }
            if (physicObject != null)
            {
                physicObject.Body.Immovable = physicObjectData.immovable;
                physicObject.Body.AllowFreezing = physicObjectData.allowFreezing;
                physicObject.CollisionType = physicObjectData.collisionType;
            }
        }

        private void FillBasicModel(CBasicModelData basicModelData, CNode dareNode)
        {
            CBasicModel basicModel = null;
            if (basicModelData.objectData != null)
                switch (basicModelData.objectData.GetType().Name)
                {
                    case "CPlaneData":
                        basicModel = dareNode.Entity.CreateBasicPlane((basicModelData.objectData as CPlaneData).size).Model as CBasicModel;
                        break;

                    case "CBoxData":
                        basicModel = dareNode.Entity.CreateBasicBox((basicModelData.objectData as CBoxData).size).Model as CBasicModel;
                        break;

                    case "CCylinderData":
                        basicModel = dareNode.Entity.CreateBasicCylinder((basicModelData.objectData as CCylinderData).length,
                                                                            (basicModelData.objectData as CCylinderData).diameter).Model as CBasicModel;
                        break;

                    case "CSphereData":
                        basicModel = dareNode.Entity.CreatePhysicSphere((basicModelData.objectData as CSphereData).diameter).Model as CBasicModel;
                        break;

                    case "CCapsuleData":
                        basicModel = dareNode.Entity.CreatePhysicCapsule((basicModelData.objectData as CCapsuleData).length,
                                                                            (basicModelData.objectData as CCapsuleData).diameter).Model as CBasicModel;
                        break;
                    case "CHeightmapData":
                        //basicModel = dareNode.Entity.CreateBasicHeightmap(dareNode.Render3D.HeightMapInfo).Model as CBasicModel;
                        //m_heightMapInfo = null;
                        break;

                    default: break;
                }
            if (basicModel != null)
            {
                basicModel.FillMode = basicModelData.fillMode;
                basicModel.CullMode = basicModelData.cullMode;
                basicModel.Orientation = Matrix.CreateFromQuaternion(basicModelData.orientation);
            }
        }

        private void FillAudioSound(List<CAudioEntityData> audioEntitiesData, CNode dareNode)
        {
            foreach (CAudioEntityData audioEnt in audioEntitiesData)
                if ((audioEnt.name != null) && (audioEnt.fullName != null))
                    dareNode.Entity.AttachSound(audioEnt.name, audioEnt.fullName);
        }

        private void FillEmitters(List<CEmitterData> emittersData, CNode dareNode)
        {
            foreach (CEmitterData emitter in emittersData)
                FillEmitter(emitter, dareNode);
        }

        private void FillEmitter(CEmitterData emitterData, CNode dareNode)
        {
            object emitter;
            emitterData.FillFromDataStructure(out emitter);
            if (emitter != null)
                dareNode.Entity.AttachEmitter(emitter as AEmitter);
        }

        private void FillSprite3D(List<CLineText3DData> lineText3DData, CNode dareNode)
        {
            foreach (CLineText3DData lineText in lineText3DData)
                FillLineText3D(lineText, dareNode);
        }

        private void FillLineText3D(CLineText3DData lineText3DData, CNode dareNode)
        {
            if (lineText3DData.name != null)
            {
                CLineText3D lineText3d = new CLineText3D(lineText3DData.name);

                lineText3d.Text = lineText3DData.text;
                lineText3d.Offset = lineText3DData.offset;
                dareNode.Entity.AttachSprite3D(lineText3d);
            }
        }

        #endregion

        #region cameras

        private void FillCameras(ref CRender3D render)
        {
            foreach (CCameraData camData in m_cameras.Values)
                FillCamera(camData, ref render);
        }

        private void FillCamera(CCameraData camData, ref CRender3D render)
        {
            switch (camData.GetType().Name)
            {
                case "CIsoCameraData":
                    CIsoCamera iso = new CIsoCamera(camData.name);
                    iso.FollowPosition = (camData as CIsoCameraData).followPosition;
                    iso.Smooth = (camData as CIsoCameraData).interpolateMove;
                    iso.SmoothAmount = (camData as CIsoCameraData).interpolateAmount;
                    //iso.(camData as CIsoCameraData).interpolateZoom;
                    iso.ZoomMax = (camData as CIsoCameraData).zoomMax;
                    iso.ZoomMin = (camData as CIsoCameraData).zoomMin;
                    iso.Zoom = (camData as CIsoCameraData).zoomValue;
                    iso.FollowNode = render.GetNode((camData as CIsoCameraData).followNode);
                    render.CameraMgr.RegisterCamera(iso);
                    if (camData.active)
                        render.CameraMgr.SetActiveCamera(iso);
                    break;
                case "CFirstPersonCameraData":
                    CFirstPersonCamera fpc = new CFirstPersonCamera(camData.name);
                    fpc.SetPosition((camData as CFirstPersonCameraData).move);
                    fpc.SetOrientation((camData as CFirstPersonCameraData).orientate);
                    render.CameraMgr.RegisterCamera(fpc);
                    if (camData.active)
                        render.CameraMgr.SetActiveCamera(fpc);
                    break;
                default:
                    CCamera cam = new CCamera(camData.name);
                    render.CameraMgr.RegisterCamera(cam);
                    if (camData.active)
                        render.CameraMgr.SetActiveCamera(cam);
                    break;
            }
        }

        #endregion

        #endregion

        #region build

        public void BuildToFile(IBuilder builder, string path, bool _override)
        {
            builder.Build<CDareDataStructuresMgr>(this, path, _override);
        }

        #endregion

        #region auto fill

        public CDareDataStructuresMgr FillDataStructure(CHUDSystem hud, CRender3D render, CGameObjectMgr gameObjectMgr, bool saveAudio)
        {
            if (render != null)
                FillRender3D(render);
            if (hud != null)
                FillHud(hud);
            if (render != null)
                FillCamera(render.CameraMgr);
            if (saveAudio)
                FillAudio();
            if (gameObjectMgr != null)
                FillGameObjects(gameObjectMgr);
            return this;
        }

        public CDareDataStructuresMgr FillAudio()
        {
            m_audioMgr.FillDataStructure();
            return this;
        }

        public CDareDataStructuresMgr FillHud(CHUDSystem hud)
        {
            HUDName = hud.Name;
            m_cursor = new CCursorData();
            m_cursor.FillDataStructure(hud.Cursor);
            foreach (CWidget wid in hud.Widgets.Values)
            {
                CWidgetData wd = CWidgetData.CreateWidgetDataType(wid);
                wd.FillDataStructure(wid);
                Widgets.Add(wd.name, wd);
            }
            return this;
        }

        public CDareDataStructuresMgr FillRender3D(CRender3D render)
        {
            m_renderName = render.Name;
            m_effectMgr = render.EffectMgr;
            foreach (CNode node in render.Nodes.Values)
            {
                if (node.Entity.Model != null && node.Entity.Model is DARE.CSubTerrainModel)
                {
                    if (m_nodes.ContainsKey(node.Parent.Name))
                        m_nodes.Remove(node.Parent.Name);
                    continue;
                }
                CNodeData nd = new CNodeData();
                nd.FillDataStructure(node);
                m_nodes.Add(nd.name, nd);
            }
            foreach (CTerrainInfo terrainInfo in render.TerrainInfos)
            {
                CTerrainData td = new CTerrainData();
                td.FillDataStructure(terrainInfo);
                m_terrains.Add(td.name, td);
            }
            return this;
        }

        public CDareDataStructuresMgr FillCamera(CCameraMgr cameraMgr)
        {
            foreach (CCamera cam in cameraMgr.GetGameCamerasList())
            {
                CCameraData camData;
                switch (cam.GetType().Name)
                {
                    case "CIsoCamera":
                        camData = new CIsoCameraData();
                        break;
                    case "CFirstPersonCamera":
                        camData = new CFirstPersonCameraData();
                        break;
                    default:
                        camData = new CCameraData();
                        break;
                }
                camData.FillDataStructure(cam);
                if (!m_cameras.ContainsKey(camData.name))
                    m_cameras.Add(camData.name, camData);
            }
            return this;
        }

        public CDareDataStructuresMgr FillGameObjects(CGameObjectMgr gameObjectMgr)
        {
            foreach (CGameObject go in gameObjectMgr.GetAllGameObjectTemplates())
            {
                AttrTypeDependentSerializable att = go.GetType().GetCustomAttributes(typeof(AttrTypeDependentSerializable), false).First() as AttrTypeDependentSerializable;
                Type t = att.TypeOfDataStructure;
                if (t == null)
                    t = typeof(CGameObjectData);
                CGameObjectData god = (CGameObjectData)t.GetConstructor(new Type[0]).Invoke(new object[0]);
                god.FillDataStructure(go);
                m_gameObjects.Add(go.Name, god);
            }

            foreach (CGameObject go in gameObjectMgr.GetAllInstances())
            {
                AttrTypeDependentSerializable att = go.GetType().GetCustomAttributes(typeof(AttrTypeDependentSerializable), false).First() as AttrTypeDependentSerializable;
                Type t = att.TypeOfDataStructure;
                if (t == null)
                    t = typeof(CGameObjectData);
                CGameObjectData god = (CGameObjectData)t.GetConstructor(new Type[0]).Invoke(new object[0]);
                god.FillDataStructure(go);
                m_instances.Add(go.ID, god);
            }

            return this;
        }

        #endregion

        #region cameras

        /// <summary>
        /// Registers a camera to be serialized.
        /// Use this method if the camera to be serialized is a CIsoCamera or a CFirstPersonCamera or a CCamera (parent class).
        /// In other cases, use CDareDataStructuresMgr.AddCamera(CCameraData) because every data that aren't stored in the CCameraData
        /// will not be serialized (to serialize your class that inherits CCamera, you have to create a class that inherits
        /// CCameraData that will be filled, then, sent with CDareDataStructuresMgr.AddCamera(CCameraData) method).
        /// </summary>
        /// <param name="cam">The IsoCamera or FirstPersonCamera or a CCamera to be serialized</param>
        public CDareDataStructuresMgr AddCamera(CCamera cam)
        {
            CCameraData cd;
            if (cam is CIsoCamera)
                cd = new CIsoCameraData();
            else if (cam is CFirstPersonCamera)
                cd = new CFirstPersonCameraData();
            else
                cd = new CCameraData();
            cd.FillDataStructure(cam);
            if (!m_cameras.ContainsKey(cd.name))
                m_cameras.Add(cd.name, cd);
            return this;
        }

        /// <summary>
        /// Registers a camera to be serialized.
        /// If the camera that you want to serialize is a CFirstPersonCamera, a CThirdPersonCamera or a simple CCamera,
        /// you can use CDareDataStructuresMgr.AddCamera(CCamera).
        /// </summary>
        /// <param name="camData">The CCameraData filled to be serialized</param>
        /// <returns></returns>
        public CDareDataStructuresMgr AddCamera(CCameraData camData)
        {
            if (!m_cameras.ContainsKey(camData.name))
                m_cameras.Add(camData.name, camData);
            return this;
        }

        public CDareDataStructuresMgr RemoveCamera(string name)
        {
            m_cameras.Remove(name);
            return this;
        }

        #endregion

        #region nodes

        /// <summary>
        /// Registers a node to be serialized.
        /// </summary>
        public CDareDataStructuresMgr AddNode(CNode node)
        {
            CNodeData nodeData = new CNodeData();
            nodeData.FillDataStructure(node);
            if (!m_nodes.ContainsKey(nodeData.name))
                m_nodes.Add(nodeData.name, nodeData);
            return this;
        }

        public CDareDataStructuresMgr RemoveNode(string name)
        {
            m_nodes.Remove(name);
            return this;
        }

        #endregion

        #region widgets

        /// <summary>
        /// Registers a hud widget to be serialized.
        /// Use this method if the CWidget to be serialized is from the DAREngine.
        /// If the CWidget that you want to serialize is your own creation (and inherits CWidget),
        /// Use the CDareDataStructuresMgr.AddHudWidget(CWidgetData) to serialize your widget.
        /// </summary>
        /// <param name="cam"></param>
        public CDareDataStructuresMgr AddHudWidget(CWidget widget)
        {
            CWidgetData wd = CWidgetData.CreateWidgetDataType(widget);
            wd.FillDataStructure(widget);
            if (!Widgets.ContainsKey(wd.name))
                Widgets.Add(wd.name, wd);
            return this;
        }

        /// <summary>
        /// Registers a hud widget to be serialized.
        /// If the widget that you want to serialize is a CWidget from the DAREngine you can use CDareDataStructuresMgr.AddHudWidget(CWidget).
        /// </summary>
        /// <param name="camData">The CCameraData filled to be serialized</param>
        /// <returns></returns>
        public CDareDataStructuresMgr AddHudWidget(CWidgetData widgetData)
        {
            if (!Widgets.ContainsKey(widgetData.name))
                Widgets.Add(widgetData.name, widgetData);
            return this;
        }

        public CDareDataStructuresMgr RemoveHudWidget(string name)
        {
            Widgets.Remove(name);
            return this;
        }

        #endregion

        #region gameObjects templates

        /// <summary>
        /// Registers a game object template to be serialized.
        /// Use this method only if the CGameObject is a basic CGameObject and not an inheritance.
        /// </summary>
        /// <param name="cam"></param>
        public CDareDataStructuresMgr AddGameObjectTemplate(CGameObject gameObject)
        {
            CGameObjectData gd = new CGameObjectData();
            gd.FillDataStructure(gameObject);
            if (!m_gameObjects.ContainsKey(gd.gameObject.Name))
                m_gameObjects.Add(gd.gameObject.Name, gd);
            return this;
        }

        /// <summary>
        /// Registers a game object template to be serialized.
        /// Use this method if the game object that you want to serialize inherits CGameObject.
        /// </summary>
        /// <param name="camData"></param>
        /// <returns></returns>
        public CDareDataStructuresMgr AddGameObjectTemplate(CGameObjectData gameObjectData)
        {
            if (!m_gameObjects.ContainsKey(gameObjectData.gameObject.Name))
                m_gameObjects.Add(gameObjectData.gameObject.Name, gameObjectData);
            return this;
        }

        public CDareDataStructuresMgr RemoveGameObjectTemplate(string name)
        {
            m_gameObjects.Remove(name);
            return this;
        }

        #endregion

        #region gameObjects instances

        /// <summary>
        /// Registers a game object instance to be serialized.
        /// Use this method only if the CGameObject is a basic CGameObject and not an inheritance.
        /// </summary>
        /// <param name="cam"></param>
        public CDareDataStructuresMgr AddGameObjectInstance(CGameObject gameObject)
        {
            CGameObjectData gd = new CGameObjectData();
            gd.FillDataStructure(gameObject);
            if (!m_instances.ContainsKey(gd.gameObject.ID))
                m_instances.Add(gd.gameObject.ID, gd);
            return this;
        }

        /// <summary>
        /// Registers a game object instance to be serialized.
        /// Use this method if the game object that you want to serialize inherits CGameObject.
        /// </summary>
        /// <param name="camData"></param>
        /// <returns></returns>
        public CDareDataStructuresMgr AddGameObjectInstance(CGameObjectData gameObjectData)
        {
            if (!m_instances.ContainsKey(gameObjectData.gameObject.ID))
                m_instances.Add(gameObjectData.gameObject.ID, gameObjectData);
            return this;
        }

        public CDareDataStructuresMgr RemoveGameObjectInstance(string name)
        {
            uint id = 0;
            foreach (CGameObjectData go in m_instances.Values)
                if (go.gameObject.Name == name)
                {
                    id = go.gameObject.ID;
                    break;
                }
            if (id != 0)
                m_instances.Remove(id);
            return this;
        }

        #endregion

        #endregion
    }
}
